#ifndef __M_SINK_H_
#define __M_SINK_H_

#include "unil.hpp"
#include <memory>
#include <fstream>
#include <cassert>
#include <ctime>
#include <sstream>

namespace TopLog
{

    class Logsink
    {
    public:
        using ptr = std::shared_ptr<Logsink>;
        Logsink(){};
        virtual ~Logsink(){};

        virtual void log(const char *data, size_t len) = 0;
    };

    class StdoutSink : public Logsink
    {
        // 写入标准输出
    public:
        void log(const char *data, size_t len)
        {
            std::cout.write(data, len);
        }
    };

    // 指定文件
    class FileSink : public Logsink
    {
    public:
        FileSink(const string &pathname)
        {
            // 创建目录
            Unil::File::CreateDirectory(Unil::File::Path(pathname));
            // 创建并打开日志文件
            _oft.open(pathname, std::ios::binary | std::ios::app);
            assert(_oft.is_open());
        };
        // 将日志消息写入标准输出
        void log(const char *data, size_t len)
        {
            _oft.write(data, len);
            assert(_oft.good());
        };

    private:
        std::string _pathname;
        std::ofstream _oft;
    };

    // 滚动文件输出
    class RollSink : public Logsink
    {
    public:
        RollSink(const string &basename, size_t max_size)
            : _basename(basename), _max_size(max_size), _now_size(0), _name_count(0)
        {
            string pathname = createNewFile();

            // 创建目录
            Unil::File::CreateDirectory(Unil::File::Path(basename));

            // 创建并打开日志文件
            _oft.open(pathname, std::ios::binary | std::ios::app);
            assert(_oft.is_open());
        };
        void log(const char *data, size_t len)
        {
            // 当前文件大小大于预设文件大小 切换文件
            if (_now_size >= _max_size)
            {
                _oft.close(); // 关闭源文件
                string pathname = createNewFile();
                _oft.open(pathname, std::ios::binary | std::ios::app);
                assert(_oft.is_open());
                _now_size = 0;
                _name_count++;
            }
            _oft.write(data, len);
            assert(_oft.good());
            _now_size += len;
        };

    private:
        string createNewFile()
        {
            time_t t = Unil::Data::Gettime();
            struct tm lt;
            localtime_r(&t, &lt); // 时间转换 时间戳->结构体
            stringstream filename;
            filename << _basename;
            filename << lt.tm_year + 1900;
            filename << lt.tm_mon + 1;
            filename << lt.tm_mday;
            filename << lt.tm_hour;
            filename << lt.tm_min;
            filename << lt.tm_sec;
            filename << "-";
            filename << _name_count;
            filename << ".log";

            return filename.str();
        };

        string _basename;   // ./log/base   -> ./lgo/base-123456789.log
        size_t _max_size;   // 记录最大文件大小，超过就要切换文件
        size_t _now_size;   // 记录当前文件已经写入的数据大小
        std::ofstream _oft; // 输出
        size_t _name_count; // 名字后缀
    };

    enum TIMEGAP
    {
        SECOND,
        MINUTE,
        HOUR,
        DAY
    };

    // 时间滚动
    class RollTimeSink : public TopLog::Logsink
    {
    public:
        RollTimeSink(const string &basename, TIMEGAP gap_type) : _basename(basename)
        {
            switch (gap_type)
            {
            case TIMEGAP::SECOND:
                _gapszie = 1;
                break;
            case TIMEGAP::MINUTE:
                _gapszie = 60;
                break;
            case TIMEGAP::HOUR:
                _gapszie = 3600;
                break;
            case TIMEGAP::DAY:
                _gapszie = 3600 * 24;
                break;
            }
            //
            _cur_gap = _gapszie == 1 ? TopLog::Unil::Data::Gettime() : TopLog::Unil::Data::Gettime() % _gapszie; // 获取当前时间是第几个时间段  %1==0
            std::string filename = createNewFile();                                                              // 创建文件
            // 创建目录
            TopLog::Unil::File::CreateDirectory(TopLog::Unil::File::Path(filename));
            // 创建并打开日志文件
            _oft.open(filename, std::ios::binary | std::ios::app);
            assert(_oft.is_open());
        };

        // 将日志消息写入标准输出
        void log(const char *data, size_t len)
        {
            time_t cur = TopLog::Unil::Data::Gettime();
            if ((cur % _gapszie) != _cur_gap) // 3600 %60 ==0
            {
                _oft.close();
                std::string filename = createNewFile();
                _oft.open(filename, std::ios::binary | std::ios::app);
                assert(_oft.is_open());
            }

            _oft.write(data, len);
            assert(_oft.good());
        };

    private:
        string createNewFile()
        {
            time_t t = TopLog::Unil::Data::Gettime();
            struct tm lt;
            localtime_r(&t, &lt); // 时间转换 时间戳->结构体
            stringstream filename;
            filename << _basename;
            filename << lt.tm_year + 1900;
            filename << lt.tm_mon + 1;
            filename << lt.tm_mday;
            filename << lt.tm_hour;
            filename << lt.tm_min;
            filename << lt.tm_sec;
            filename << ".log";

            return filename.str();
        };
        std::string _basename;
        std::ofstream _oft;
        std::size_t _gapszie;
        std::size_t _cur_gap;
    };

    // 将所写的功能 一并返回
    class SinkFactory
    {
    public:
        template <typename SinkType, typename... Args>
        static Logsink::ptr create(Args &&...args)
        {
            return std::make_shared<SinkType>(std::forward<Args>(args)...); // 完美转发
        }
    };

}

#endif